import { BufferAttribute, BufferGeometry, GLBufferAttribute, InterleavedBufferAttribute, Matrix4, Vector2, Vector3 } from "../../thirdparty/three/imports";
import { BufferAttribute3D } from "./BufferAttribute3D";
import { GLBufferAttribute3D } from "./GLBufferAttribute3D";
import { InterleavedBufferAttribute3D } from "./InterleavedBufferAttribute3D";

export class BufferGeometry3D
{
    /**
     * @private
     */
    get _geometry()
    {
        if (!this.__geometry)
        {
            this.__geometry = new BufferGeometry();
        }

        return this.__geometry;
    }
    protected __geometry: BufferGeometry;

    get attributes():
        {
            [name: string]: BufferAttribute3D | InterleavedBufferAttribute3D | GLBufferAttribute3D;
        }
    {
        const attributes = this._geometry.attributes;

        const attribute3ds: {
            [name: string]: BufferAttribute3D | InterleavedBufferAttribute3D | GLBufferAttribute3D;
        } = {};
        for (const key in attributes)
        {
            const element = attributes[key];
            if (element instanceof BufferAttribute)
            {
                attribute3ds[key] = BufferAttribute3D.get(element);
            }
            else if (element instanceof InterleavedBufferAttribute)
            {
                attribute3ds[key] = InterleavedBufferAttribute3D.get(element);
            }
            else
            {
                attribute3ds[key] = GLBufferAttribute3D.get(element);
            }
        }

        return attribute3ds;
    }
    set attributes(v)
    {
        const attributes: {
            [name: string]: BufferAttribute | InterleavedBufferAttribute | GLBufferAttribute;
        } = {};

        for (const key in v)
        {
            const element = v[key];
            attributes[key] = element._bufferAttribute;
        }

        this._geometry.attributes = attributes;
    }

    setAttribute(name: string, attribute: BufferAttribute3D | InterleavedBufferAttribute3D | GLBufferAttribute3D)
    {
        this._geometry.setAttribute(name, attribute._bufferAttribute);
    }

    getAttribute(name: string)
    {
        const attribute = this._geometry.getAttribute(name);

        let bufferAttribute3D: BufferAttribute3D | InterleavedBufferAttribute3D | GLBufferAttribute3D;
        if (attribute.constructor === BufferAttribute)
        {
            bufferAttribute3D = BufferAttribute3D.get(attribute);
        }
        else if (attribute.constructor === InterleavedBufferAttribute)
        {
            bufferAttribute3D = InterleavedBufferAttribute3D.get(attribute);
        }
        else if (attribute.constructor === GLBufferAttribute)
        {
            bufferAttribute3D = GLBufferAttribute3D.get(attribute);
        }

        return bufferAttribute3D;
    }

    center()
    {
        this._geometry.center();

        return this;
    }

    deleteAttribute(name: string)
    {
        this._geometry.deleteAttribute(name);

        return this;
    }

    dispose()
    {
        this._geometry.dispose();
    }

    rotateX(angle: number)
    {
        this._geometry.rotateX(angle);

        return this;
    }

    rotateY(angle: number)
    {
        this._geometry.rotateY(angle);

        return this;
    }

    translate(x: number, y: number, z: number)
    {
        this._geometry.translate(x, y, z);

        return this;
    }

    applyMatrix4(matrix: Matrix4)
    {
        this._geometry.applyMatrix4(matrix);

        return this;
    }

    protected _invalidate()
    {
        this.__geometry?.dispose();
        this.__geometry = null;
    }

    setFromPoints(points: Vector3[] | Vector2[])
    {
        this._geometry.setFromPoints(points);

        return this;
    }

    computeVertexNormals()
    {
        this._geometry.computeVertexNormals();
    }

    scale(x: number, y: number, z: number)
    {
        this._geometry.scale(x, y, z);

        return this;
    }

    computeTangents()
    {
        this._geometry.computeTangents();
    }

    computeBoundingSphere()
    {
        this._geometry.computeBoundingSphere();
    }

    clone()
    {
        const bufferGeometry = this._geometry.clone();
        const bufferGeometry3D = BufferGeometry3D.get(bufferGeometry);

        return bufferGeometry3D;
    }

    get boundingSphere()
    {
        return this._geometry.boundingSphere;
    }

    get boundingBox()
    {
        return this._geometry.boundingBox;
    }

    computeBoundingBox()
    {
        this._geometry.computeBoundingBox();
    }

    static get(geometry: BufferGeometry)
    {
        let geometry3D = this._map.get(geometry) as BufferGeometry3D;
        if (!geometry3D)
        {
            geometry3D = new BufferGeometry3D();
            geometry3D.__geometry = geometry;
            this._map.set(geometry, geometry3D);
        }

        return geometry3D;
    }
    protected static _map = new Map<BufferGeometry, BufferGeometry3D>();
}
